#include<sys/types.h>
#include<sys/socket.h>
#include<netinet/in.h>
#include<arpa/inet.h>
#include<assert.h>
#include<iostream>
#include<errno.h>
#include<unistd.h>
#include<string.h>
#include<fcntl.h>
#include<poll.h>

using namespace std;

#define USER_LIMIT 10
#define BUFFER_SIZE 64
#define FD_LIMIT 65535

struct  client_data
{
    /* client_data */
    sockaddr_in address;
    char* write_buf;
    char buf[BUFFER_SIZE];
};

int setNonBlocking(int fd){
    int old_option = fcntl(fd,F_GETFL);
    int new_option = old_option | O_NONBLOCK;
    fcntl(fd,F_SETFL,new_option);

    return old_option;
}

int main(){
    const char* ip = "192.168.30.128";
    int port = atoi("1235");
    int ret = 0;
    int userCounter = 0;

    struct sockaddr_in _address;
    bzero(&_address,sizeof(_address));
    _address.sin_family = AF_INET;
    _address.sin_port = htons(port);
    inet_pton(AF_INET,ip,&_address.sin_addr);

    int _listenFD = socket(PF_INET,SOCK_STREAM,0);
    if(_listenFD < 0){
        cout<<"Server socket init ERROR! "<<endl;
        return -1;
    }
    cout<<"Server socket init SUCCESS! "<<endl;

    ret = bind(_listenFD,(struct sockaddr*)&_address,sizeof(_address));
    if(ret == -1){
        cout<<"Server bind ERROR!"<<endl; 
        return -1;
    }
    cout<<"Server bind SUCCESS!"<<endl; 

    ret = listen(_listenFD,5);
    if(ret == -1){
        cout<<"Server listen ERROR!"<<endl; 
        return -1;
    }
    cout<<"Server listening..."<<endl; 

    client_data* users = new client_data[FD_LIMIT];
    
    pollfd fds[USER_LIMIT+1];
    for(int i=1;i<=USER_LIMIT;++i){
        fds[i].fd = -1;
        fds[i].events = 0;
    }
    fds[0].fd = _listenFD;
    fds[0].events = POLLIN | POLLERR;
    fds[0].revents = 0;

    while (1)
    {
        ret = poll(fds,userCounter+1,-1);
        if(ret < 0){
            cout<<"Poll ERROR! "<<endl;
            break;
        }
        //cout<<"Poll SUCCESS!"<<endl;

        for(int i = 0 ; i < userCounter + 1 ; ++i){
            if((fds[i].fd ==_listenFD) && (fds[i].revents & POLLIN)){
                struct sockaddr_in _client_address;
                socklen_t clientaddrlength = sizeof(_client_address);
                int _connectFD = accept(_listenFD,(struct sockaddr*)&_client_address,&clientaddrlength);
                if(_connectFD < 0){
                    cout<<"Server accept new client ERROR!"<<endl;
                    continue;
                }
                cout<<"Server accept new client SUCCESS!"<<endl;

                if(userCounter >= USER_LIMIT){
                    const char * info="Too many users!";
                    cout<<info<<endl;
                    send(_connectFD,info,strlen(info),0);
                    close(_connectFD);
                    continue;
                }

                userCounter++;
                users[_connectFD].address = _client_address;
                setNonBlocking(_connectFD);
                fds[userCounter].fd = _connectFD;
                fds[userCounter].events = POLLIN | POLLRDHUP | POLLERR;
                fds[userCounter].revents = 0;
                cout<<"New client come in , now we have "<<userCounter<<" users."<<endl;
            }
            else if(fds[i].revents & POLLERR){
                cout<<"Get an ERROR from "<<fds[i].fd<<endl;
                continue;
            }
            else if(fds[i].revents & POLLRDHUP){
                users[fds[i].fd] = users[fds[userCounter].fd];
                close(fds[i].fd);
                fds[i] = fds[userCounter];
                i--;
                userCounter--;
                cout<<"A client left."<<endl;
            }
            else if(fds[i].revents & POLLIN){
                int  _connectFD = fds[i].fd;
                memset(users[_connectFD].buf,'\0',BUFFER_SIZE);
                ret = recv(_connectFD,users[_connectFD].buf,BUFFER_SIZE-1,0);
                cout<<"Client "<<_connectFD<<" : "<<users[_connectFD].buf<<" . size is "<<ret<<" bytes."<<endl;
                
                if(ret < 0){
                    if(errno != EAGAIN){
                        close(_connectFD);
                        users[fds[i].fd]=users[fds[userCounter].fd];
                        fds[i] = fds[userCounter];
                        i--;
                        userCounter--;
                    }
                }
                else if(ret == 0){

                }
                else{
                    for(int j = 1;j <= userCounter;++j){
                        if(fds[j].fd == _connectFD)
                            continue;
                        fds[j].events |= ~POLLIN;
                        fds[j].events |= POLLOUT;
                        users[fds[j].fd].write_buf = users[_connectFD].buf;
                    }
                }
            }
            else if(fds[i].revents & POLLOUT){
                int _connectFD = fds[i].fd;
                if(!users[_connectFD].write_buf)
                    continue;
                ret = send(_connectFD,users[_connectFD].write_buf,strlen(users[_connectFD].write_buf),0);
                users[_connectFD].write_buf=NULL;
                fds[i].events |= ~POLLOUT;
                fds[i].events |= POLLIN;
            }
        }
    }
    
    delete [] users;
    close(_listenFD);

    return 0;
}